<template>
  <!-- 富文本编辑器 -->
    <div style="border: 1px solid #ccc;border-bottom: none;">
      <Toolbar style="border-bottom: 1px solid #ccc"
        :editor="editorRef"
        :defaultConfig="toolbarConfig"
      />
      <Editor 
        v-model="valueHtml"
        :defaultConfig="editorConfig"
        @onCreated="handleCreated"
      />
    </div>
</template>

<script lang="ts" setup>
import '@wangeditor/editor/dist/css/style.css' // 引入 css

import { onBeforeUnmount, ref, shallowRef, onMounted, watch, provide } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { IEditorConfig } from '@wangeditor/editor'
import api from '@/api';
import path from "@/api/path.js";
import { SlateElement } from '@wangeditor/editor'

type ImageElement = SlateElement & {
    src: string
    alt: string
    url: string
    href: string
}
type VideoElement = SlateElement & {
    src: string
    poster?: string
}
type InsertFnType_pic = (url: string, alt: string, href: string) => void
type InsertFnType_video = (url: string, poster: string ) => void


//#region 自定义的一些数据
  const fileList = ref([])
  const jwtToken = localStorage.getItem('wx_access-client'); //在本地存储中取出jwt令牌（由登录操作存储于本地）
//#endregion


//#region wangEditor 自带的内容，稍作修改即可
// 编辑器实例，必须用 shallowRef
const editorRef = shallowRef()



// 内容 HTML
const valueHtml = ref()


onMounted(() => {
  //修改操作时，外部会传进来值，要把外部的值（即内容）展现到编辑区域
  if(model.value != null){
      //这里一定要异步调用来获取外部数据
      setTimeout(() => {
        valueHtml.value = model.value
      }, 200)
  }

})


const toolbarConfig = {}
//#region 关于修改配置
  // 1、初始化 MENU_CONF 属性
  const editorConfig: Partial<IEditorConfig> = {  // JS 语法: const editorConfig = {   
      MENU_CONF: {}
  }

  // 2、配置开始
  editorConfig.placeholder = '请输入内容...'
  editorConfig.autoFocus = false // 是否自动聚焦到编辑区域

  //2.1.1关于插入图片的配置
  const tempUrl = ref([]) //临时的图片地址
  const imageList = ref([]) //已经插入或上传的图片
  editorConfig.MENU_CONF['insertImage'] = {
      //插入图片之后的回调
      onInsertedImage(imageNode: ImageElement | null) {
          if (imageNode == null) return
          const { src, alt, url, href } = imageNode
          imageList.value.push(src)
      },
  }

  // 2.1.2关于上传图片的配置！！！
  editorConfig.MENU_CONF['uploadImage'] = {
      server: path.baseUrl + '/common/upload', //后端接口地址
      fieldName: 'file', //后端自己规定的文件名字

      // 自定义上传参数，例如传递验证的 token 等。参数会被添加到 formData 中，一起上传到服务端。
      meta: {
        fullPath: 'img/client/', //图片上传到哪一个文件夹，后端自己规定的
      },
      // 自定义增加 http  header
      headers: {
        token: jwtToken,
      },

      maxFileSize: 2 * 1024 * 1024, //单个文件的最大体积限制，不设置的话默认就为 2M


    // 自定义插入图片（后端返回的格式如果不符合官方文档的规范，就这样做）
    customInsert(res: any, insertFn: InsertFnType_pic) {  
        let url =  res.data // res 即服务端的返回结果
        insertFn(url, "图片不见了", null) // 从 res 中找到 url(图片 src ，必须) alt(图片描述文字，非必须) href(图片的链接，非必须) ，然后插入图片
    },

    // 上传之前触发
    onBeforeUpload(file: File) { 
       
    },

    // 单个文件上传成功之后
    onSuccess(file: File, res: any) {
        console.log(`${file.name} 上传成功`, res)

        console.log("文件路径为：",res.data)
        
        api.addTempFile(res.data) //将文件路径存入后端的临时文件表里
    },

    // 单个文件上传失败
    onFailed(file: File, res: any) {
        console.log(`${file.name} 上传失败`, res)
    },

    // 上传错误，或者触发 timeout 超时
    onError(file: File, err: any, res: any) {
        console.log(`${file.name} 上传出错`, err, res)
    },


    //【注意】不需要修改的不用写，wangEditor 会去 merge 当前其他配置
  }


  //2.2.1关于插入视频的配置
  const videoList = ref([]) //已经插入或上传的视频
  editorConfig.MENU_CONF['insertVideo'] = {
    //插入视频之后的回调
    onInsertedVideo(videoNode: VideoElement | null) {
      if (videoNode == null) return
        const { src } = videoNode
        videoList.value.push(src) //已经上传到服务器的视频的地址数组
    },
  }

  //2.2.2关于上传视频的配置！！！
  editorConfig.MENU_CONF['uploadVideo'] = {
    server: path.baseUrl + '/common/upload', //后端接口地址
    // form-data fieldName ，默认值 'wangeditor-uploaded-video'
    fieldName: 'file', //要改成后端规定的名字

    // 单个文件的最大体积限制，默认为 10M
    maxFileSize: 5 * 1024 * 1024, // 5M

    // 最多可上传几个文件，默认为 5
    maxNumberOfFiles: 3,

    // 选择文件时的类型限制，默认为 ['video/*'] 。如不想限制，则设置为 []
    allowedFileTypes: ['video/*'],

    // 自定义上传参数，例如传递验证的 token 等。参数会被添加到 formData 中，一起上传到服务端。
    meta: {
        fullPath: 'video/client/', //文件上传到哪一个文件夹，后端自己规定的
    },
    // 自定义增加 http  header
    headers: {
        token: jwtToken,
    },

    // 将 meta 拼接到 url 参数中，默认 false
    metaWithUrl: false,

    // 超时时间，默认为 30 秒
    timeout: 15 * 1000, // 15 秒

    // 视频不支持 base64 格式插入


    // 自定义插入视频
    customInsert(res: any, insertFn: InsertFnType_video) {
      let url =  res.data // res 即服务端的返回结果
      insertFn(url, null) // poster是视频封面图片 url ，可选
    },


    // 单个文件上传成功之后
    onSuccess(file: File, res: any) {  // TS 语法
        console.log(`${file.name} 上传成功`, res)
    },

    // 单个文件上传失败
    onFailed(file: File, res: any) {  // TS 语法
        console.log(`${file.name} 上传失败`, res)
    },

    // 上传错误，或者触发 timeout 超时
    onError(file: File, err: any, res: any) {  // TS 语法
        console.log(`${file.name} 上传出错`, err, res)
    },
}
//#endregion


// 组件销毁时，也及时销毁编辑器
onBeforeUnmount(() => {
    const editor = editorRef.value
    if (editor == null) return
    editor.destroy()
})

//编辑器内容变化时触发
const handleCreated = (editor) => {
  editorRef.value = editor // 记录 editor 实例，重要！
}
//#endregion



//#region 关于组件间传值
  const model = defineModel()

  //监听编辑器内部的内容变化，同步给外部
  watch(() => valueHtml.value, (newValue, oldValue) => {
    model.value = newValue
  })
  //监听外部的model值，同步给内部
  watch(() => model.value, (newValue, oldValue) => {
    if(newValue == null){ newValue = '' } // valueHtml.value为null时会报错，不能为null
    valueHtml.value = newValue
  })

  //获取当前编辑器的所有图片
  const getImgMethod = ()=>{
    return editorRef.value.getElemsByType('image')
  } 
  //获取当前编辑器的所有视频
  const getVideoMethod = ()=>{
    return editorRef.value.getElemsByType('video')
  }


  // 暴露给父组件的方法或属性
  defineExpose({imageList,videoList,fileList,getImgMethod,getVideoMethod}) 


//#endregion


</script> 
